#include "gameArea.h"
#include <gdkmm/general.h>
#include <iostream>

gameArea::keyDirection gameArea::mKeyDir=gameArea::None;
double gameArea::PFSpeed=1.5;

gameArea::gameArea() :
    m_live(6),
    m_source(0),
    m_Vspace(50),
    diH(10,300),
    diV(50,100),
    diType(1,100),
    m_gameStatus(gameStatus::start)
{
    mBoll=new boll();
    set_size_request(400,400);
    
    try
    {
        imgBackGround=Gdk::Pixbuf::create_from_file("../background.png");
        imgPFiro=Gdk::Pixbuf::create_from_file("../PLiro.png");
    }
    catch(const Gdk::PixbufError& e)
    {
        std::cerr << e.what() << '\n';
    }

    if(imgBackGround==nullptr)
        std::cerr<<"Error unable to load image: backgroud.png"<<std::endl;
    if(imgPFiro==nullptr)
        std::cerr<<"ERROR unable to load image: PFiro.png"<<std::endl;

    set_draw_func(sigc::mem_fun(*this,&gameArea::on_drawMain));    

    flushTimeout=Glib::signal_timeout().connect(sigc::mem_fun(*this,&gameArea::on_timeout),20);

    //init game items
    double PFlastPos=20;
    for(int i=0;i<8;i++)
    {
        PFlastPos+=diV(dre);
        loopListP.push_back
        (std::unique_ptr<platform>(new platform(blockType::stone,diH(dre),PFlastPos,this)));
    }
}

gameArea::~gameArea()
{
    if(mBoll!=nullptr)
        delete mBoll;
}

void gameArea::on_drawMain(const Cairo::RefPtr<Cairo::Context>& cr,int width,int height)
{
    switch (m_gameStatus)
    {
        case run:
            on_draw(cr,width,height);
            break;
        case stop:
            on_drawFrame(cr,width,height);
            break;
        case start:
            on_drawFrame(cr,width,height);
            Pango::FontDescription font;
            font.set_family("Source Han Serif CN");
            font.set_weight(Pango::Weight::BOLD);
            auto layout=create_pango_layout("按任意键开始");
            layout->set_font_description(font);
            int text_height,text_width;
            layout->get_pixel_size(text_width,text_height);
            cr->save();
            cr->set_source_rgb(222.0/255.0,99.0/255.0,109.0/255.0);
            cr->move_to((width-text_width)/2,(height-text_height)/2);
            layout->show_in_cairo_context(cr);
            cr->stroke();
            cr->restore();
            if(mKeyDir!=keyDirection::None)
                m_gameStatus=run;
            break;
    }
}

void gameArea::gameAreaInit()
{
    m_live=6;
    m_source=0;

    delete mBoll;
    mBoll=new boll();

    loopListP.clear();
    double PFlastPos=20;
    for(int i=0;i<8;i++)
    {
        PFlastPos+=diV(dre);
        loopListP.push_back
        (std::unique_ptr<platform>(new platform(blockType::stone,diH(dre),PFlastPos,this)));
    }
    mKeyDir=None;

    m_gameStatus=gameStatus::run;
}

bool gameArea::on_timeout()
{
    queue_draw();
    return true;
}

void gameArea::on_drawFrame(const Cairo::RefPtr<Cairo::Context>& cr,int width,int height)
{
    if(!imgBackGround)
        return;
    Gdk::Cairo::set_source_pixbuf(cr,imgBackGround,0,0);
    cr->paint();
}

void gameArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr,int width,int height)
{
    on_drawFrame(cr,width,height);

    if((*loopListP.begin())->getYaxis()<=20)
    {
        loopListP.erase(loopListP.begin());
        auto ptrEndElem=loopListP.end();
        ptrEndElem--;
        double releaseV=(*ptrEndElem)->getYaxis()+diV(dre);
        if(diType(dre)<20)
            loopListP.push_back(std::unique_ptr<platform>(new platform(blockType::iron,diH(dre),releaseV,this)));
        else
            loopListP.push_back(std::unique_ptr<platform>(new platform(blockType::stone,diH(dre),releaseV,this)));
    }
    mBoll->move();

    bool isColIro=false;
    bool unCol=true;
    for(auto & i:loopListP)
    {
        i->move();
        i->draw_W(cr,width,height);
        if(unCol && mBoll->is_collision(*i))
        {
            if(i->m_type==blockType::iron)
                isColIro=true;
            if(! i->isCollisioned)
            {
                i->isCollisioned=true;
                m_source+=10;
                if(m_source%500==0)
                    PFSpeed+=0.5;
                m_signalSourceChange.emit(m_source);
            }
            unCol=false;
        }
    }
    if(mBoll->Yaxis-mBoll->i_radius<20 || mBoll->Yaxis+mBoll->i_radius>400 || isColIro)
    {
        m_live--;
        if(m_live<=0)
        {
            m_signalFinish.emit();
            m_gameStatus=gameStatus::stop;
            return;
        }
        else
        {
            m_signalLiveChange.emit(m_live);
            auto itr=loopListP.begin();
            itr++; itr++;
            mBoll->Yaxis = (*itr)->getYaxis() + mBoll->i_radius;
            if((*itr)->getXaxis()>200.0)
                mBoll->Xaxis=(*itr)->getXaxis()-50;   
            else
                mBoll->Xaxis=(*itr)->getXaxis()+120;
        }
    }
    mBoll->draw_W(cr,width,height);
}

gameArea::boll::boll() :
    g_time(1),
    Xaxis(200),
    Yaxis(200),
    i_radius(10),
    moveSpeed(2.75)
{
}

gameArea::boll::~boll() {}

void gameArea::boll::draw_W(const Cairo::RefPtr<Cairo::Context>& cr,int width,int height)
{
    cr->save();
    cr->set_source_rgb(0.749019608,0.211764706,0.141176471);
    cr->arc(Xaxis,Yaxis,i_radius,0,2.0*M_PI);
    cr->fill_preserve();
    cr->stroke();
    cr->restore();
}

void gameArea::boll::move()
{
    g_time+=0.8;
    resultMoveY=g_time*2;   //a=g*t
    double resultMoveX=0;
    switch (gameArea::mKeyDir)
    {
        case gameArea::keyDirection::Left:
                resultMoveX=-moveSpeed;
            break;
        case gameArea::keyDirection::Right:
                resultMoveX=moveSpeed;
        default:
            break;
    }
    Xaxis+=resultMoveX;
    Yaxis+=resultMoveY;
}

bool gameArea::boll::is_collision(platform & pf)
{
    //top
    if(pf.Yaxis-pf.i_height/2-i_radius-resultMoveY<=Yaxis && pf.Xaxis-i_radius<=Xaxis && pf.Xaxis+pf.i_widget+i_radius>=Xaxis && pf.Yaxis>=Yaxis)
    {
        Yaxis-=Yaxis-pf.Yaxis+pf.i_height/2+i_radius;
        g_time=1;
        return true;
    }
    //left
    else if(pf.Xaxis-i_radius<Xaxis && pf.Xaxis>Xaxis && Yaxis>pf.Yaxis-pf.i_height/2 && Yaxis<pf.Yaxis+pf.i_height/2)
    {
        Xaxis-=Xaxis-pf.Xaxis+i_radius+pf.i_height;
        g_time=1;
        return true;
    }
    //right
    else if(pf.Xaxis+pf.i_widget+i_radius>Xaxis && pf.Xaxis+pf.i_widget<Xaxis && pf.Yaxis-pf.i_height/2<Yaxis && pf.Yaxis+pf.i_height/2>Yaxis)
    {
        Xaxis+=pf.Xaxis+pf.i_widget+i_radius-Xaxis;
        g_time=1;
        return true;
    }
    //frame
    if(Xaxis<10+i_radius)
        Xaxis=10+i_radius;
    else if(Xaxis>390-i_radius)
        Xaxis=390-i_radius;
    return false;
}

gameArea::platform::platform(blockType type,double X,double Y,gameArea* parentThis=nullptr) :
    m_type(type),
    Xaxis(X),
    Yaxis(Y),
    i_widget(90),
    i_height(10),
    isCollisioned(false)
{
    if(parentThis==nullptr)
        std::cerr<<"WARING: unknow parent object"<<std::endl;
    parentPtr=parentThis;
}

gameArea::platform::~platform() {}

void gameArea::platform::draw_W(const Cairo::RefPtr<Cairo::Context>& cr,int width,int height)
{
    switch (m_type)
    {
    case blockType::stone :
        cr->save();
        cr->set_line_width(i_height);
        cr->set_source_rgb(121.0/255.0,130.0/255.0,53.0/255.0);
        cr->move_to(Xaxis,Yaxis);
        cr->line_to(Xaxis+i_widget,Yaxis);
        cr->stroke();
        cr->restore();
        break;
    case blockType::iron :
        cr->save();
        Gdk::Cairo::set_source_pixbuf(cr,parentPtr->imgPFiro,Xaxis,Yaxis-5);
        cr->paint();
        cr->stroke();
        cr->restore();
        break;
    default:
        break;
    }
}

void gameArea::platform::move()
{
    Yaxis-=PFSpeed;
}